package com.grandtech.insurance.common.utils.trans;

import com.google.gson.JsonObject;
import com.grandtech.insurance.common.utils.MathUtil;
import com.grandtech.insurance.common.utils.StringUtil;
import com.mapbox.geojson.*;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.io.WKBReader;
import com.vividsolutions.jts.io.WKTReader;
import com.vividsolutions.jts.io.WKTWriter;
import org.geotools.geojson.geom.GeometryJSON;

import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Created by zy on 2018/4/12.
 */

public final class Transformation {

    public static Map<Integer, String> WKID = new SimpleMap<Integer, String>()
            .push(75, "4513").push(78, "4514").push(81, "4515").push(84, "4516")
            .push(87, "4517").push(90, "4518").push(93, "4519").push(96, "4520")
            .push(99, "4521").push(102, "4522").push(105, "4523").push(108, "4524")
            .push(111, "4525").push(114, "4526").push(117, "4527").push(120, "4528")
            .push(123, "4529").push(126, "4530").push(129, "4531").push(132, "4532")
            .push(135, "4533");

    /**
     * GeoJson转JTS Geometry
     *
     * @param geoJSON
     * @return
     */
    public static com.vividsolutions.jts.geom.Geometry geoJson2JstGeometry(GeoJson geoJSON) {
        if (geoJSON == null) return null;
        String _geoJSON = geoJSON.toJson();
        return geoJsonStr2JstGeometry(_geoJSON);
    }

    /**
     * GeoJson String 转JTS Geometry
     *
     * @param geoJSON
     * @return
     */
    public static com.vividsolutions.jts.geom.Geometry geoJsonStr2JstGeometry(String geoJSON) {
        if (geoJSON == null) return null;
        com.vividsolutions.jts.geom.Geometry geometry;
        GeometryJSON geometryJSON = new GeometryJSON(9);
        Reader reader = new StringReader(geoJSON);
        try {
            geometry = geometryJSON.read(reader);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return geometry;
    }

    public static GeoJson geoJsonStr2GeoJson(String geoJSON) {
        com.vividsolutions.jts.geom.Geometry geometry = geoJsonStr2JstGeometry(geoJSON);
        return jstGeometry2GeoJson(geometry);
    }

    /**
     * jstGeometry转GeoJson
     *
     * @param geometry
     * @return
     */
    public static GeoJson jstGeometry2GeoJson(com.vividsolutions.jts.geom.Geometry geometry) {
        if (geometry == null) return null;
        String wkt = jstGeometry2Wkt(geometry);
        return wkt2BoxGeometry(wkt);
    }

    public static String jstGeometry2Wkt(com.vividsolutions.jts.geom.Geometry geometry) {
        try {
            if (geometry == null) return null;
            WKTWriter wktWriter = new WKTWriter();
            StringWriter stringWriter = new StringWriter();
            wktWriter.write(geometry, stringWriter);
            String wkt = stringWriter.toString();
            return wkt;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * GeoJson转wkt
     *
     * @param geometry
     * @return
     */
    public static String boxGeometry2Wkt(GeoJson geometry) {
        if (geometry == null) return null;
        String _geoJSON = geometry.toJson();
        com.vividsolutions.jts.geom.Geometry _geometry = geoJsonStr2JstGeometry(_geoJSON);
        return _geometry.toText();
    }

    public static BoundingBox getBoxRatio(BoundingBox boundingBox, double ratio) {
        double west = boundingBox.west();
        double east = boundingBox.east();
        double south = boundingBox.south();
        double north = boundingBox.north();
        double dixX, dixY;
        if (ratio >= 1) { //表示放大
            dixX = (ratio - 1) * (east - west) / 2;
            dixY = (ratio - 1) * (north - south) / 2;
            return BoundingBox.fromLngLats(west - dixX, south - dixY, east + dixX, north + dixY);
        } else { //表示缩小
            dixX = (1 - ratio) * (east - west) / 2;
            dixY = (1 - ratio) * (north - south) / 2;
            return BoundingBox.fromLngLats(west + dixX, south + dixY, east - dixX, north - dixY);
        }
    }

    public static BoundingBox getGeoJsonBox(GeoJson geoJson) {
        GeoJson _geoJson = null;
        if (geoJson instanceof Feature) {
            _geoJson = ((Feature) geoJson).geometry();
        } else if (geoJson instanceof Geometry) {
            _geoJson = geoJson;
        }
        if (_geoJson == null) return null;
        com.vividsolutions.jts.geom.Geometry geometry = geoJson2JstGeometry(_geoJson);
        Envelope envelope = null;
        if (geometry instanceof com.vividsolutions.jts.geom.Point) {
            com.vividsolutions.jts.geom.Point point = (com.vividsolutions.jts.geom.Point) geometry;
            Coordinate coordinate[] = geometry.getCoordinates();
            double x = 0;
            double y = 0;
            for (int i = 0; i < geometry.getCoordinates().length; i++) {
                x = coordinate[i].x;
                y = coordinate[i].y;
            }
           return BoundingBox.fromPoints(Point.fromLngLat(x,y),Point.fromLngLat(x,y));
        } else {
            envelope = geometry.getEnvelopeInternal();
        }
        return BoundingBox.fromLngLats(envelope.getMinX(), envelope.getMinY(), envelope.getMaxX(), envelope.getMaxY());
    }

    public static String getSimpleBoxStr(GeoJson geoJson) {
        BoundingBox boundingBox = getGeoJsonBox(geoJson);
        String bounds = boundingBox.west() + "," + boundingBox.south() + "," + boundingBox.east() + "," + boundingBox.north();
        return bounds;
    }

    public static String getSimpleBoxRatioStr(GeoJson geoJson, double ratio) {
        BoundingBox boundingBox = getBoxRatio(geoJson, ratio);
        String bounds = boundingBox.west() + "," + boundingBox.south() + "," + boundingBox.east() + "," + boundingBox.north();
        return bounds;
    }

    public static String mergeBoxFeatureBoundStr(double ratio, GeoJson... geoJsons) {
        BoundingBox boundingBox = mergeBoxFeatureBound(ratio, geoJsons);
        String bounds = boundingBox.west() + "," + boundingBox.south() + "," + boundingBox.east() + "," + boundingBox.north();
        return bounds;
    }

    public static BoundingBox mergeBoxFeatureBound(double ratio, GeoJson... geoJsons) {
        if (geoJsons == null || geoJsons.length == 0) return null;
        GeoJson geoJson;
        BoundingBox boundingBox;
        double east = Double.MIN_VALUE, north = Double.MIN_VALUE, west = Double.MAX_VALUE, south = Double.MAX_VALUE;
        for (int i = 0; i < geoJsons.length; i++) {
            geoJson = geoJsons[i];
            boundingBox = getGeoJsonBox(geoJson);
            if (east < boundingBox.east()) {
                east = boundingBox.east();
            }
            if (north < boundingBox.north()) {
                north = boundingBox.north();
            }
            if (west > boundingBox.west()) {
                west = boundingBox.west();
            }
            if (south > boundingBox.south()) {
                south = boundingBox.south();
            }
        }
        BoundingBox box = BoundingBox.fromLngLats(west, south, east, north);
        return getBoxRatio(box, ratio);
    }

    public static String boxGeometry2BoxLatLngBoundsString(Geometry geo) {
        try {
            BoundingBox b = boxGeometry2BoxLatLngBounds(geo);
            if (b != null) {
                String szzb = "W:" + MathUtil.getDecimal(b.west(), "#.000000")
                        + ";S:" + MathUtil.getDecimal(b.south(), "#.000000") +
                        ";E:" + MathUtil.getDecimal(b.east(), "#.000000") + ";N:"
                        + MathUtil.getDecimal(b.north(), "#.000000");
                return szzb;
            }
            return "";
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String getCodesByGeometry(Geometry geometry) {
        if (geometry == null) {
            return "";
        }
        String wkt = Transformation.boxGeometry2Wkt(geometry);
        return wkt2Codes(wkt);
    }

    /**
     * wkt返回坐标串(支持POLYGON单端，多段，MULTIPOLYGON)
     *
     * @param codes
     * @return
     */
    public static String wkt2Codes(String codes) {
        try {
            System.out.println("处理前geometry-------" + codes);
            String codes_r = "";
            String[] codesarr;
            /*********** 将坐标串通用化格式处理都变成(111 222,333 444),(666 77,66 44)**************************/
            if (codes.startsWith("POLYGON")) {
                codes = codes.replace("POLYGON(", "");
                codes = codes.replace("POLYGON (", "");
                codes = codes.substring(0, codes.length() - 1);
            }
            if (codes.startsWith("MULTIPOLYGON")) {
                codes = codes.replace("MULTIPOLYGON((", "");
                codes = codes.replace("MULTIPOLYGON ((", "");
                codes = codes.substring(0, codes.length() - 2);
                codes = codes.replace(")),((", "),(");
            }
            /***********(111 222,333 444),(666 77,66 44)格式数据转化为数组codesarr[(111,222;333,444),(666,77;66,44)]**************************/
            codes = codes.replace("),(", ")|(");
            codes = codes.replace("), (", ")|(");
            codes = codes.replace(", ", ";");
            codes = codes.replace(",", ";");
            codes = codes.replace(" ", ",");
            codesarr = codes.split("\\|");
            /***********循环将每个坐标xy取出来保留六位**************************/
            for (int i = 0; i < codesarr.length; i++) {
                String c = codesarr[i];
                /***********转换成111,222;333,444**************************/
                c = c.replace("(", "").replace(")", "");
                String cA = "";
                String[] carr = c.split(";");
                for (int j = 0; j < carr.length; j++) {
                    String s = carr[j];
                    String[] sarr = s.split(",");
                    cA = cA + StringUtil.round(Double.valueOf(sarr[0]), 6) + ",";
                    cA = cA + StringUtil.round(Double.valueOf(sarr[1]), 6) + ";";
                }
                cA = cA.substring(0, cA.length() - 1);
                codes_r = codes_r + "(" + cA + "),";
            }
            codes_r = codes_r.substring(0, codes_r.length() - 1);
            if (codesarr.length == 1) {
                codes_r = codes_r.replace("(", "").replace(")", "");
            }
            return codes_r;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("处理错误------" + codes);
            return codes;
        }
    }

    /**
     * 缩放外包矩形(外包矩形都是正的，没有斜的)
     *
     * @param geoJson
     * @param ratio   放大率(边长)
     * @return
     */
    public static BoundingBox getBoxRatio(GeoJson geoJson, double ratio) {
        BoundingBox boundingBox = getGeoJsonBox(geoJson);
        double minX = boundingBox.west();
        double maxX = boundingBox.east();
        double minY = boundingBox.south();
        double maxY = boundingBox.north();
        /*if(!boundingBox.northeast().equals("") && !boundingBox.southwest().equals("")){
            return BoundingBox.fromPoints(boundingBox.southwest(),boundingBox.northeast());
        }*/

        double dixX, dixY;
        if (ratio >= 1) { //表示放大
            dixX = (ratio - 1) * (maxX - minX) / 2;
            dixY = (ratio - 1) * (maxY - minY) / 2;
            return BoundingBox.fromLngLats(minX - dixX, minY - dixY, maxX + dixX, maxY + dixY);
        } else { //表示缩小
            dixX = (1 - ratio) * (maxX - minX) / 2;
            dixY = (1 - ratio) * (maxY - minY) / 2;
            return BoundingBox.fromLngLats(minX + dixX, minY + dixY, maxX - dixX, maxY - dixY);
        }
    }


    /**
     * WKB 转 GeoJson MultiPolygon
     *
     * @param bytes
     * @return
     * @throws Exception
     */
    public static GeoJson wkb2BoxMultiPolygon(byte[] bytes) throws Exception {
        WKBReader geomReader = new WKBReader();
        StringWriter writer = new StringWriter();
        GeometryJSON geometryJSON = new GeometryJSON(9);
        geometryJSON.write(geomReader.read(bytes), writer);
        return MultiPolygon.fromJson(writer.toString());
    }

    /**
     * WKB 转 GeoJson MultiPolygon
     *
     * @param bytes
     * @return
     * @throws Exception
     */
    public static GeoJson wkb2BoxMultiPolygon(WKBReader geomReader, GeometryJSON geometryJSON, byte[] bytes) throws Exception {
        StringWriter writer = new StringWriter();
        geometryJSON.write(geomReader.read(bytes), writer);
        return MultiPolygon.fromJson(writer.toString());
    }

    /**
     * wkb转GeoJson字符串
     */
    public static String wkb2BoxJsonString(WKBReader geomReader, GeometryJSON geometryJSON, byte[] bytes) throws Exception {
        StringWriter writer = new StringWriter();
        geometryJSON.write(geomReader.read(bytes), writer);
        return writer.toString();
    }

    public static String wkb2BoxJsonString(byte[] bytes) throws Exception {
        WKBReader wkbReader = new WKBReader();
        StringWriter writer = new StringWriter();
        GeometryJSON geometryJSON = new GeometryJSON(9);
        com.vividsolutions.jts.geom.Geometry geometry = wkbReader.read(bytes);
        geometryJSON.write(geometry, writer);
        return writer.toString();
    }

    /**
     * wkt转GeoJson
     *
     * @param wkt
     * @return
     */
    public static Geometry wkt2BoxGeometry(String wkt) {
        if (wkt == null) return null;
        String upperWkt = wkt.toUpperCase();
        try {
            if (upperWkt.startsWith("MULTIPOLYGON")) {
                return wkt2BoxMultiPolygon(wkt);
            }
            if (upperWkt.startsWith("POLYGON")) {
                return wkt2BoxPolygon(wkt);
            }
            if (upperWkt.startsWith("MULTIPOINT")) {
                return wkt2BoxMultiPoint(wkt);
            }
            if (upperWkt.startsWith("POINT")) {
                return wkt2BoxPoint(wkt);
            }
            if (upperWkt.startsWith("MULTILINESTRING")) {
                return wkt2BoxMultiLineString(wkt);
            }
            if (upperWkt.startsWith("LINESTRING")) {
                return wkt2BoxLineString(wkt);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static enum BoxGeometryType {
        MULTIPOLYGON,
        POLYGON,
        MULTIPOINT,
        POINT,
        MULTILINESTRING,
        LINESTRING
    }

    public static BoxGeometryType getBoxGeometryType(GeoJson geoJson) {
        String wkt = boxGeometry2Wkt(geoJson);
        if (wkt == null) return null;
        String upperWkt = wkt.toUpperCase();
        try {
            if (upperWkt.startsWith("MULTIPOLYGON")) {
                return BoxGeometryType.MULTIPOLYGON;
            }
            if (upperWkt.startsWith("POLYGON")) {
                return BoxGeometryType.POLYGON;
            }
            if (upperWkt.startsWith("MULTIPOINT")) {
                return BoxGeometryType.MULTIPOINT;
            }
            if (upperWkt.startsWith("POINT")) {
                return BoxGeometryType.POINT;
            }
            if (upperWkt.startsWith("MULTILINESTRING")) {
                return BoxGeometryType.MULTILINESTRING;
            }
            if (upperWkt.startsWith("LINESTRING")) {
                return BoxGeometryType.LINESTRING;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Geometry wkt2BoxPolygonCenter(String wkt) {
        try {
            com.vividsolutions.jts.geom.Geometry geometry = wkt2JtsGeometry(wkt);
            com.vividsolutions.jts.geom.Point point = geometry.getCentroid();
            return Point.fromLngLat(point.getX(), point.getY());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    public static com.vividsolutions.jts.geom.Geometry wkt2JtsGeometry(String wkt) throws Exception {
        WKTReader wktReader = new WKTReader();
        return wktReader.read(wkt);
    }

    /**
     * WKT 转 GeoJson MultiPolygon
     *
     * @param wkt
     * @return
     * @throws Exception
     */
    public static MultiPolygon wkt2BoxMultiPolygon(String wkt) throws Exception {
        WKTReader wktReader = new WKTReader();
        StringWriter writer = new StringWriter();
        GeometryJSON geometryJSON = new GeometryJSON(9);
        geometryJSON.write(wktReader.read(wkt), writer);
        return MultiPolygon.fromJson(writer.toString());
    }

    public static Polygon wkt2BoxPolygon(String wkt) throws Exception {
        WKTReader wktReader = new WKTReader();
        StringWriter writer = new StringWriter();
        GeometryJSON geometryJSON = new GeometryJSON(9);
        geometryJSON.write(wktReader.read(wkt), writer);
        return Polygon.fromJson(writer.toString());
    }

    public static Point wkt2BoxPoint(String wkt) throws Exception {
        WKTReader wktReader = new WKTReader();
        StringWriter writer = new StringWriter();
        GeometryJSON geometryJSON = new GeometryJSON(9);
        geometryJSON.write(wktReader.read(wkt), writer);
        return Point.fromJson(writer.toString());
    }

    public static MultiPoint wkt2BoxMultiPoint(String wkt) throws Exception {
        WKTReader wktReader = new WKTReader();
        StringWriter writer = new StringWriter();
        GeometryJSON geometryJSON = new GeometryJSON(9);
        geometryJSON.write(wktReader.read(wkt), writer);
        return MultiPoint.fromJson(writer.toString());
    }

    public static LineString wkt2BoxLineString(String wkt) throws Exception {
        WKTReader wktReader = new WKTReader();
        StringWriter writer = new StringWriter();
        GeometryJSON geometryJSON = new GeometryJSON(9);
        geometryJSON.write(wktReader.read(wkt), writer);
        return LineString.fromJson(writer.toString());
    }

    public static MultiLineString wkt2BoxMultiLineString(String wkt) throws Exception {
        WKTReader wktReader = new WKTReader();
        StringWriter writer = new StringWriter();
        GeometryJSON geometryJSON = new GeometryJSON(9);
        geometryJSON.write(wktReader.read(wkt), writer);
        return MultiLineString.fromJson(writer.toString());
    }

    public static Feature _2Feature(String wkt, Map<String, Object> map) {
        Geometry geometry = wkt2BoxGeometry(wkt);
        JsonObject properties = map2JsonObject(map);
        return Feature.fromGeometry(geometry, properties);
    }


    public static JsonObject map2JsonObject(Map<String, Object> map) {
        JsonObject properties = new JsonObject();
        Object val;
        for (String key : map.keySet()) {
            val = map.get(key);
            if (val == null) continue;
            if (val instanceof String) {
                properties.addProperty(key, (String) val);
            }
            if (val instanceof Double) {
                properties.addProperty(key, (Double) val);
            }
            if (val instanceof Float) {
                properties.addProperty(key, (Float) val);
            }
            if (val instanceof Integer) {
                properties.addProperty(key, (Integer) val);
            }
            if (val instanceof Number) {
                properties.addProperty(key, (Number) val);
            }
            if (val instanceof Boolean) {
                properties.addProperty(key, (Boolean) val);
            }
            if (val instanceof Character) {
                properties.addProperty(key, (Character) val);
            }
        }
        return properties;
    }

    public static GeoJson geoJson2CenterPoint(GeoJson geoJson) {
        if (geoJson == null) return null;
        com.vividsolutions.jts.geom.Geometry geometry = geoJson2JstGeometry(geoJson);
        if (geometry == null) return null;
        com.vividsolutions.jts.geom.Geometry center = geometry.getCentroid();
        return jstGeometry2GeoJson(center);
    }

    public static String geoJson2NodeSymbol(GeoJson geoJson) {
        if (geoJson == null) return null;
        if (geoJson instanceof Feature) {
            geoJson = ((Feature) geoJson).geometry();
        }
        com.vividsolutions.jts.geom.Geometry _geometry = geoJson2JstGeometry(geoJson);
        if (_geometry == null) return null;
        //中心位置
        com.vividsolutions.jts.geom.Geometry center = _geometry.getCentroid();
        Coordinate centerCoordinate = center.getCoordinate();
        Coordinate[] nodeCoordinate = _geometry.getCoordinates();

        List<Feature> list = new ArrayList<>();
        Feature feature;
        Point point;
        Geometry geometry;
        JsonObject jsonObject;
        geometry = Point.fromLngLat(centerCoordinate.x, centerCoordinate.y);
        jsonObject = new JsonObject();
        point = (Point) geometry;
        jsonObject.addProperty("symbol", String.format("%.6f", point.latitude()) + "," + String.format("%.6f", point.longitude()));
        feature = Feature.fromGeometry(geometry, jsonObject);
        list.add(feature);
        for (int i = 0; i < nodeCoordinate.length; i++) {
            geometry = Point.fromLngLat(nodeCoordinate[i].x, nodeCoordinate[i].y);
            point = (Point) geometry;
            jsonObject = new JsonObject();
            jsonObject.addProperty("symbol", String.format("%.6f", point.latitude()) + "," + String.format("%.6f", point.longitude()));
            feature = Feature.fromGeometry(geometry, jsonObject);
            list.add(feature);
        }
        FeatureCollection featureCollection = FeatureCollection.fromFeatures(list);
        String s = featureCollection.toJson();
        return s;
    }

    public static BoundingBox boxGeometry2BoxLatLngBounds(Geometry geo) {
        try {
            com.vividsolutions.jts.geom.Geometry geometry = wkt2JtsGeometry(boxGeometry2Wkt(geo));
            Envelope envelope = geometry.getEnvelopeInternal();
            BoundingBox latLngBounds = BoundingBox.fromLngLats(envelope.getMinX(), envelope.getMinY(), envelope.getMaxX(), envelope.getMaxY());
            return latLngBounds;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 算面积
     *
     * @param geoJson
     * @return
     */
    public static Double compute84GeoJsonArea(Object geoJson) {
        if (geoJson == null) return null;
        Geometry geometry = null;
        if (geoJson instanceof Feature) {
            Feature feature = (Feature) geoJson;
            geometry = feature.geometry();
        } else if (geoJson instanceof Geometry) {
            geometry = (Geometry) geoJson;
        } else if (geoJson instanceof String) {
            geometry = (Geometry) geoJsonStr2GeoJson(geoJson.toString());
        }
        com.vividsolutions.jts.geom.Geometry _2000Geometry = geoJson2JstGeometry(geometry);
        if (_2000Geometry == null) return 0.0d;
        return _84JtsGeometry_2_2000(_2000Geometry);
    }


    /**
     * 计算面积 返回(mu)
     *
     * @param geometry
     * @return
     */
    public static Double _84JtsGeometry_2_2000(com.vividsolutions.jts.geom.Geometry geometry) {
        Double area = 0d;
        if (geometry == null) return area;
        com.vividsolutions.jts.geom.Geometry _2000Geometry = Pro4jUtil.proJtsGeometry84To2000(geometry);
        if (_2000Geometry == null) return area;
        area = _2000Geometry.getArea();
        return area * 0.0015;
    }
}
